package com.framework.system.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.framework.core.common.bo.LoginUser;
import com.framework.core.common.enums.DeletedEnum;
import com.framework.core.common.enums.StatusEnum;
import com.framework.core.common.enums.YesNoEnum;
import com.framework.core.exception.CustomException;
import com.framework.core.util.SecurityUtil;
import com.framework.system.entity.*;
import com.framework.system.entity.dto.SysUserAddDTO;
import com.framework.system.entity.dto.SysUserQueryDTO;
import com.framework.system.entity.dto.SysUserResetPwdDTO;
import com.framework.system.entity.dto.SysUserUpdateDTO;
import com.framework.system.entity.vo.*;
import com.framework.system.mapper.SysDeptMapper;
import com.framework.system.mapper.SysUserMapper;
import com.framework.system.mapper.SysUserPostMapper;
import com.framework.system.mapper.SysUserRoleMapper;
import com.framework.system.service.ISysRoleService;
import com.framework.system.service.ISysUserService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author Kevin
 * @since 2021-06-10
 */
@Service
@AllArgsConstructor
public class SysUserServiceImpl implements ISysUserService {

    private SysUserMapper sysUserMapper;

    private SysUserRoleMapper sysUserRoleMapper;

    private SysUserPostMapper sysUserPostMapper;

    private SysDeptMapper sysDeptMapper;

    private ISysRoleService sysRoleService;

    @Override
    public Page<SysUserListVO> selectSysUserWithPage(Page reqPage, SysUserQueryDTO req) {
        List<SysDept> sysDeptList = new ArrayList();
        if (Objects.nonNull(req.getDeptId())) {
            sysDeptList = sysDeptMapper.selectList(Wrappers.lambdaQuery(SysDept.class).apply("find_in_set({0}, ancestors)", req.getDeptId()));
        }
        List<String> sysDeptIdList = sysDeptList.stream().map(item -> String.valueOf(item.getId())).collect(Collectors.toList());
        LambdaQueryWrapper<SysUser> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.like(StrUtil.isNotBlank(req.getAccount()), SysUser::getAccount, req.getAccount());
        queryWrapper.like(StrUtil.isNotBlank(req.getNickname()), SysUser::getNickname, req.getNickname());
        queryWrapper.like(StrUtil.isNotBlank(req.getRealname()), SysUser::getRealname, req.getRealname());
        queryWrapper.like(StrUtil.isNotBlank(req.getEnglishName()), SysUser::getEnglishName, req.getEnglishName());
        queryWrapper.like(StrUtil.isNotBlank(req.getEmail()), SysUser::getEmail, req.getEmail());
        queryWrapper.like(StrUtil.isNotBlank(req.getPhone()), SysUser::getPhone, req.getPhone());
        queryWrapper.like(StrUtil.isNotBlank(req.getStaffNumber()), SysUser::getStaffNumber, req.getStaffNumber());
        queryWrapper.eq(SysUser::getStatus, StatusEnum.YES.getCode());
        queryWrapper.and(Objects.nonNull(req.getDeptId()), wrapper -> wrapper.eq(SysUser::getDeptId, req.getDeptId())
                .or(!CollectionUtils.isEmpty(sysDeptIdList), wrapper2 -> wrapper2.in(SysUser::getDeptId, sysDeptIdList)));
        queryWrapper.orderByAsc(SysUser::getCreateTime);
        Page<SysUser> page = sysUserMapper.selectPage(reqPage, queryWrapper);
        Page<SysUserListVO> voPage = BeanUtil.copyProperties(page, Page.class);
        voPage.setRecords(BeanUtil.copyToList(page.getRecords(), SysUserListVO.class));
        voPage.getRecords().forEach(item -> {
            SysDept sysDept = sysDeptMapper.selectById(item.getDeptId());
            item.setDeptName(Objects.isNull(sysDept) ? null : sysDept.getDeptName());
        });
        return voPage;
    }

    @Override
    public List<SysUserListVO> selectSysUserWithList(SysUserQueryDTO req) {
        LambdaQueryWrapper<SysUser> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.like(StrUtil.isNotBlank(req.getAccount()), SysUser::getAccount, req.getAccount());
        queryWrapper.like(StrUtil.isNotBlank(req.getNickname()), SysUser::getNickname, req.getNickname());
        queryWrapper.like(StrUtil.isNotBlank(req.getRealname()), SysUser::getRealname, req.getRealname());
        queryWrapper.like(StrUtil.isNotBlank(req.getEnglishName()), SysUser::getEnglishName, req.getEnglishName());
        queryWrapper.like(StrUtil.isNotBlank(req.getEmail()), SysUser::getEmail, req.getEmail());
        queryWrapper.like(StrUtil.isNotBlank(req.getPhone()), SysUser::getPhone, req.getPhone());
        queryWrapper.like(StrUtil.isNotBlank(req.getStaffNumber()), SysUser::getStaffNumber, req.getStaffNumber());
        queryWrapper.like(Objects.nonNull(req.getDeptId()), SysUser::getDeptId, req.getDeptId());
        queryWrapper.eq(SysUser::getStatus, StatusEnum.YES.getCode());
        queryWrapper.orderByAsc(SysUser::getCreateTime);
        List<SysUser> list = sysUserMapper.selectList(queryWrapper);
        List<SysUserListVO> voList = BeanUtil.copyToList(list, SysUserListVO.class);
        voList.forEach(item -> {
            SysDept sysDept = sysDeptMapper.selectById(item.getDeptId());
            item.setDeptName(Objects.isNull(sysDept) ? null : sysDept.getDeptName());
        });
        return voList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addSysUser(SysUserAddDTO req) {
        if (!this.checkUniqueAccount(req.getAccount(), null)) {
            throw new CustomException("账号已存在");
        }
        if (!this.checkUniqueEmail(req.getEmail(), null)) {
            throw new CustomException("邮箱已存在");
        }
        if (!this.checkUniquePhone(req.getPhone(), null)) {
            throw new CustomException("手机号已存在");
        }
        if (!this.checkUniqueStaffNumber(req.getStaffNumber(), null)) {
            throw new CustomException("工号已存在");
        }
        SysUser entity = BeanUtil.copyProperties(req, SysUser.class);
        // 密码加密
        entity.setPassword(SecurityUtil.encryptPassword(entity.getPassword()));
        entity.setPswModified(YesNoEnum.NO.getCode());
        sysUserMapper.insert(entity);

        // 删除用户与岗位关联
        sysUserPostMapper.delete(Wrappers.lambdaQuery(SysUserPost.class).eq(SysUserPost::getUserId, entity.getId()));
        // 新增用户与岗位关联
        if (Objects.nonNull(req.getPostIds())) {
            req.getPostIds().stream().map(item -> {
                SysUserPost sysUserPost = new SysUserPost();
                sysUserPost.setUserId(entity.getId());
                sysUserPost.setPostId(item);
                return sysUserPost;
            }).forEach(sysUserPostMapper::insert);
        }

        // 删除用户与角色关联
        sysUserRoleMapper.delete(Wrappers.lambdaQuery(SysUserRole.class).eq(SysUserRole::getUserId, entity.getId()));
        // 新增用户与角色关联
        if (Objects.nonNull(req.getRoleIds())) {
            req.getRoleIds().stream().map(item -> {
                SysUserRole sysUserRole = new SysUserRole();
                sysUserRole.setUserId(entity.getId());
                sysUserRole.setRoleId(item);
                return sysUserRole;
            }).forEach(sysUserRoleMapper::insert);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateSysUser(SysUserUpdateDTO req) {
        if (!this.checkUniqueAccount(req.getAccount(), req.getId())) {
            throw new CustomException("账号已存在");
        }
        if (!this.checkUniqueEmail(req.getEmail(), req.getId())) {
            throw new CustomException("邮箱已存在");
        }
        if (!this.checkUniquePhone(req.getPhone(), req.getId())) {
            throw new CustomException("手机号已存在");
        }
        if (!this.checkUniqueStaffNumber(req.getStaffNumber(), req.getId())) {
            throw new CustomException("工号已存在");
        }
        // 当前用户不是超级管理员，不允许修改
        if (!SecurityUtil.getSysUser().getId().equals(req.getId()) && SecurityUtil.isRootGroup(req.getId())) {
            throw new CustomException("超管账户不允许修改");
        }
        SysUser entity = BeanUtil.copyProperties(req, SysUser.class);
        sysUserMapper.updateById(entity);

        // 删除用户与岗位关联
        sysUserPostMapper.delete(Wrappers.lambdaQuery(SysUserPost.class).eq(SysUserPost::getUserId, entity.getId()));
        // 新增用户与岗位关联
        if (Objects.nonNull(req.getPostIds())) {
            req.getPostIds().stream().map(item -> {
                SysUserPost sysUserPost = new SysUserPost();
                sysUserPost.setUserId(entity.getId());
                sysUserPost.setPostId(item);
                return sysUserPost;
            }).forEach(sysUserPostMapper::insert);
        }

        // 删除用户与角色关联
        sysUserRoleMapper.delete(Wrappers.lambdaQuery(SysUserRole.class).eq(SysUserRole::getUserId, entity.getId()));
        // 新增用户与角色关联
        if (Objects.nonNull(req.getRoleIds())) {
            req.getRoleIds().stream().map(item -> {
                SysUserRole sysUserRole = new SysUserRole();
                sysUserRole.setUserId(entity.getId());
                sysUserRole.setRoleId(item);
                return sysUserRole;
            }).forEach(sysUserRoleMapper::insert);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeSysUser(String ids) {
        List<String> idList = Arrays.asList(ids.split(","));
        idList.forEach(item -> {
            if (SecurityUtil.isRootGroup(Long.valueOf(item))) {
                throw new CustomException("超管账户不允许删除");
            }
            SysUser sysUserTemp = new SysUser();
            sysUserTemp.setId(Long.parseLong(item));
            sysUserTemp.setDeleted(DeletedEnum.YES.getCode());
            sysUserMapper.updateById(sysUserTemp);
        });
    }

    @Override
    public SysUserVO getSysUser(String id) {
        SysUser entity = sysUserMapper.selectById(id);
        SysUserVO sysUserVO = BeanUtil.copyProperties(entity, SysUserVO.class);
        List<SysUserRole> sysUserRoleList = sysUserRoleMapper.selectList(Wrappers.lambdaQuery(SysUserRole.class)
                .eq(SysUserRole::getUserId, entity.getId()));
        sysUserVO.setRoleIds(sysUserRoleList.stream().map(item -> item.getRoleId().toString()).collect(Collectors.toList()));

        List<SysUserPost> sysUserPostList = sysUserPostMapper.selectList(Wrappers.lambdaQuery(SysUserPost.class)
                .eq(SysUserPost::getUserId, entity.getId()));
        sysUserVO.setPostIds(sysUserPostList.stream().map(item -> item.getPostId().toString()).collect(Collectors.toList()));
        return sysUserVO;
    }

    @Override
    public void resetPwd(SysUserResetPwdDTO req) {
        SysUser entity = BeanUtil.copyProperties(req, SysUser.class);
        // 密码加密
        entity.setPassword(SecurityUtil.encryptPassword(entity.getPassword()));
        entity.setPswModified(YesNoEnum.YES.getCode());
        sysUserMapper.updateById(entity);
    }

    @Override
    public UserInfoVO getUserInfo() {
        UserInfoVO userInfoVO = new UserInfoVO();
        SysUser sysUser = sysUserMapper.selectById(StpUtil.getLoginIdAsLong());
        // 查询角色列表
        List<SysRole> sysRoleList = sysRoleService.selectSysRoleByUserId(sysUser.getId());
        List<RoleInfoVO> roleInfoVOList = sysRoleList.stream().map(item -> {
            RoleInfoVO roleInfoVO = new RoleInfoVO();
            roleInfoVO.setRoleName(item.getRoleName());
            roleInfoVO.setValue(item.getRoleCode());
            return roleInfoVO;
        }).collect(Collectors.toList());
        userInfoVO.setUserId(sysUser.getId().toString());
        userInfoVO.setUsername(sysUser.getAccount());
        userInfoVO.setRealName(sysUser.getNickname());
        userInfoVO.setAvatar(sysUser.getAvatar());
        userInfoVO.setDesc(sysUser.getRemarks());
        userInfoVO.setRoles(roleInfoVOList);
        return userInfoVO;
    }

    @Override
    public Set<String> selectPermCodeWithList() {
        return SecurityUtil.getLoginUser().getPermissions();
    }

    @Override
    public AccountInfoVO getAccountInfo() {
        LoginUser loginUser = SecurityUtil.getLoginUser();
        SysUser sysUser = sysUserMapper.selectById(loginUser.getSysUser().getId());
        return BeanUtil.copyProperties(sysUser, AccountInfoVO.class);
    }

    @Override
    public void saveAccountInfo(AccountInfoVO req) {
        LoginUser loginUser = SecurityUtil.getLoginUser();
        SysUser entity = BeanUtil.copyProperties(req, SysUser.class);
        entity.setId(loginUser.getSysUser().getId());
        sysUserMapper.updateById(entity);
    }

    private Boolean checkUniqueAccount(String value, Long id) {
        if (StrUtil.isBlank(value)) {
            return true;
        }
        id = Objects.isNull(id) ? -1L : id;
        SysUser entity = sysUserMapper.selectOne(Wrappers.lambdaQuery(SysUser.class)
                .select(SysUser::getId, SysUser::getAccount)
                .eq(SysUser::getAccount, value));
        return Objects.isNull(entity) || entity.getId().longValue() == id.longValue();
    }

    private Boolean checkUniqueEmail(String value, Long id) {
        if (StrUtil.isBlank(value)) {
            return true;
        }
        id = Objects.isNull(id) ? -1L : id;
        SysUser entity = sysUserMapper.selectOne(Wrappers.lambdaQuery(SysUser.class)
                .select(SysUser::getId, SysUser::getEmail)
                .eq(SysUser::getEmail, value));
        return Objects.isNull(entity) || entity.getId().longValue() == id.longValue();
    }

    private Boolean checkUniquePhone(String value, Long id) {
        if (StrUtil.isBlank(value)) {
            return true;
        }
        id = Objects.isNull(id) ? -1L : id;
        SysUser entity = sysUserMapper.selectOne(Wrappers.lambdaQuery(SysUser.class)
                .select(SysUser::getId, SysUser::getPhone)
                .eq(SysUser::getPhone, value));
        return Objects.isNull(entity) || entity.getId().longValue() == id.longValue();
    }

    private Boolean checkUniqueStaffNumber(String value, Long id) {
        if (StrUtil.isBlank(value)) {
            return true;
        }
        id = Objects.isNull(id) ? -1L : id;
        SysUser entity = sysUserMapper.selectOne(Wrappers.lambdaQuery(SysUser.class)
                .select(SysUser::getId, SysUser::getStaffNumber)
                .eq(SysUser::getStaffNumber, value));
        return Objects.isNull(entity) || entity.getId().longValue() == id.longValue();
    }

}
